Skip to content

Beam attenuation#35

Open
Daniel Henriksen (dihenriksen) wants to merge 23 commits into
mainfrom
beam-attenuation
Open

Beam attenuation#35
Daniel Henriksen (dihenriksen) wants to merge 23 commits into
mainfrom
beam-attenuation

Conversation

@dihenriksen
Copy link
Copy Markdown
Contributor

No description provided.

@dihenriksen Daniel Henriksen (dihenriksen) marked this pull request as ready for review May 12, 2026 02:55
Copy link
Copy Markdown

@jwlodek Jakub Wlodek (jwlodek) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked some suggestions. Mainly I'd recommend making both device classes Movables and re-work the current methods for changing states into a set method override.

Comment thread src/cditools/attenuator.py Outdated
return np.exp(-self.linear_atten_coefficient * self.thickness_cm)


class AttenuatorBank(StandardReadable, EpicsDevice):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class AttenuatorBank(StandardReadable, EpicsDevice):
class AttenuatorBank(StandardReadable, EpicsDevice, Movable[float]):

Comment thread src/cditools/attenuator.py Outdated
*(a.get_status() for _, a in self.attenuators.items())
)

async def set_attenuation(self, target_attenuation: float):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async def set_attenuation(self, target_attenuation: float):
from ophyd_async.core import AsyncStatus
@AsyncStatus.wrap
async def set(self, target_attenuation: float):

I suggest making this top level device a Movable. This way, in a plan, you can do:

yield from bps.mv(attenuator_bank, 20.0)

or directly via the RunEngine:

RE(bps.mv(attenuator_bank. 20.0))

instead of calling set_attenuation directly. The benefit if this is that you can do things like pause and wait at this step if the beam goes down, you can "dry-run" a plan without actually moving anything, and generally you get all the tooling around plans this way. It also allows you to call this and either have it block and wait for it to be done, or add it to a group of other moves and wait for them all in parallel.

class Attenuator(EpicsDevice):
filter_material = "Al"
filter_material_z = 13
filter_density = xraylib.ElementDensity(filter_material_z) # g/cm³
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these always these values, or can they be changed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are static for now and cannot change for the existing filters. Garth plans to get more filters and I don't know if they will be aluminum or something else.

Comment thread src/cditools/attenuator.py Outdated
HIGH = "High" # on


class Attenuator(EpicsDevice):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class Attenuator(EpicsDevice):
class Attenuator(EpicsDevice, Movable[AttenuatorEnum]):

Similar to the higher level class, I'd recommend making this a Movable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ophyd-async, this would be an AsyncMovable right?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/cditools/attenuator.py Outdated
"""
self.prefix = prefix
self.num = num
self.cmd = epics_signal_w(AttenuatorEnum, f"{self.prefix}:DO{self.num + 1}-Cmd")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the in_status signal the one that represents the actual physical state of the attenuator? If yes, I'd recommend consolidating these into one epics_signal_rw, where you set the read and write pvs to different values. Then, the set method can just call await self.cmd.set(value). In this scenario, maybe I'd rename the signal to state or position.

In this case I assume that the open/close operations are pretty quick, but still, generally we want to make sure we reach our target before continuing, unless the user explicitly says not to. Basically, We want:

yield from bps.mv(attenuator, AttenuatorEnum.LOW)

to block by default until we have confirmation that the attenuator has reached its target position.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants